//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2005
// Garmin and MapSource are registered trademarks or trademarks of Garmin Ltd. or one of its subsidiaries.
//
// Garmin TDB parser.
//

# include "StdAfx.h"
# include "GarminTdb.h"
# include "GarminTypes.h"
# include "Map.h"
# include "Load.h"
# include "Log.h"
# include "Status.h"

class CGarminTdbLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);
};

DECLARE_MAP_LOADER (CGarminTdbLoader, "garmin-tdb", "tdb", "Garmin MapSource map index (*.tdb)");

bool CGarminTdbLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	return _cFileSize > sizeof (CTdbProduct) && _pData [0] == 'P';
}

static
void CreateRgn (
	map_t * _pMap, size_t _cStart, size_t _cLen, file_ref_t * _pFileRef,
	RGN_KIND _Kind, USHORT _ushType,
	const char * _strLabel, const char * _strComment,
	float _x0, float _y0, float _x1, float _y1
) {
	rgn_t & rgn = _pMap->CreateRgn (_Kind);

	// Initialize the rgn.
	rgn.cBeginPos = _cStart;
	rgn.cEndPos = _cStart + _cLen;
	rgn.cFileLine = -1;
//	rgn.cOrdinal = m_cOrdinal ++;
	rgn.pFileRef = _pFileRef;

	rgn.SetType (_pMap->pTypeSet, _ushType, _Kind);

	if (_strLabel)
		rgn.strLabel = _strLabel;
	if (_strComment)
		rgn.strComment = _strComment;

	rgn.elements.resize (1);
	rgn_element_t & element = rgn.elements [0];
	element.cFileOffset = rgn.cBeginPos;
	element.cLevel = 0;
	element.bHole = false;

	element.points.resize (_Kind == rkPolyline ? 5 : 4);
	element.points [0].x = _x0;
	element.points [0].y = _y0;
	element.points [1].x = _x1;
	element.points [1].y = _y0;
	element.points [2].x = _x1;
	element.points [2].y = _y1;
	element.points [3].x = _x0;
	element.points [3].y = _y1;
	if (_Kind == rkPolyline) {
		element.points [4].x = _x0;
		element.points [4].y = _y0;
	}
}

LoadStatus_t CGarminTdbLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	const char * const pEnd = _pData + _cFileSize;

	file_refs_t file_refs;
	file_refs.push_back (file_ref_t ());
	file_refs.back ().strFileName = _strFileName;
	file_refs.back ().bAttachment = false;

	_pMap->cHeaderLen = 0;
	_pMap->pTypeSet = & g_tsGarmin;

	SetProgressStatus (0);

	const char * pRecord = _pData;
	do {
		if (g_bStop)
			return lsInterrupted;

		const CTdbRecordHdr * const pHdr = reinterpret_cast<const CTdbRecordHdr *> (pRecord);
		const char * const pNextRecord = pRecord + sizeof (CTdbRecordHdr) + pHdr->wLen;

		switch (pHdr->btType) {
			case 'P': { // Product info.
				const CTdbProduct * const pProduct = static_cast<const CTdbProduct *> (pHdr);
				ReportText ("* Product id:  \t %d", pProduct->dwProductId);
				ReportText ("* Product ver: \t %d.%02d", pProduct->dwVer/100, pProduct->dwVer%100);
				ReportText ("* Product name:\t %s", pProduct->strName);

				const CTdbProduct2 * const pProduct2 = reinterpret_cast<const CTdbProduct2 *> (pProduct->strName + ::strlen (pProduct->strName) + 1);
				ReportText ("* Data version:\t %d.%02d", pProduct2->wDataVer/100, pProduct2->wDataVer%100);
				ReportText ("* CD set name: \t %s", pProduct2->strCDSetName);

				break;
			}

			case 'D': { // Copyright strings.
				const CTdbCopyrights * const pCopyrights = static_cast<const CTdbCopyrights *> (pHdr);
				const CTdbCopyright * pCopyright = & pCopyrights->c0;
				while (true) {
					if (pCopyright->btType == 6)
						ReportText ("* Copyright #%d:\t %s", pCopyright->wOrdinal, pCopyright->strText);
					else if (pCopyright->btType == 7)
						ReportText ("* Logo #%d:     \t %s", pCopyright->wOrdinal, pCopyright->strText);

					const char * const pNextCopyrightData = pCopyright->strText + ::strlen (pCopyright->strText) + 1;
					if (pNextCopyrightData >= pNextRecord)
						break;
					pCopyright = reinterpret_cast<const CTdbCopyright *> (pNextCopyrightData);
				}

				break;
			}

			case 'R': { // ?? trademarks ??
				const CTdbRegs * const pRegs = static_cast<const CTdbRegs *> (pHdr);
				const CTdbReg * pReg = & pRegs->r0;
				while (true) {
					ReportText ("* Trademark #%d:\t %s", pReg->btOrdinal, pReg->strText);

					const char * const pNextRegData = pReg->strText + ::strlen (pReg->strText) + 1;
					if (pNextRegData >= pNextRecord)
						break;
					pReg = reinterpret_cast<const CTdbReg *> (pNextRegData);
				}
				break;
			}

			case 'B': { // Whole map.
				const CTdbImgRef * const pArea = static_cast<const CTdbImgRef *> (pHdr);
				break;
			}

			case 'L': { // Detail map ref.
				const CTdbImgRef * const pRef = static_cast<const CTdbImgRef *> (pHdr);

				if (_pMap->cHeaderLen == 0)
					_pMap->cHeaderLen = pRecord - _pData;

				char strLabel [32];
				::sprintf (strLabel, "%08d.img", pRef->dwImgId);

				const float x0 = pRef->lWest*180.f/(1UL << 31);
				float       x1 = pRef->lEast*180.f/(1UL << 31);
				if (x0 - x1 > 180)
					x1 += 360;
				const float y0 = pRef->lSouth*180.f/(1UL << 31);
				const float y1 = pRef->lNorth*180.f/(1UL << 31);

				CreateRgn (_pMap, reinterpret_cast<const char *> (pRef) - _pData, pRef->wLen, & file_refs.back (), rkPolyline, 0x1c, NULL,     NULL,                 x0, y0, x1, y1);
				CreateRgn (_pMap, reinterpret_cast<const char *> (pRef) - _pData, pRef->wLen, & file_refs.back (), rkPolygon,  0x4a, strLabel, pRef->strDisplayName, x0, y0, x1, y1);

				const CTdbImgRef2 * const pRef2 = reinterpret_cast<const CTdbImgRef2 *> (pRef->strDisplayName + ::strlen (pRef->strDisplayName) + 1);
				for (size_t c = 0; c < pRef2->wCount; ++ c) {
					//pRef2->dwSectionSize [c];
				}
				const char * const pRef2End = reinterpret_cast<const char *> (& pRef2->dwSectionSize [pRef2->wCount]);
				assert (pRef2End [0] == 0x01);
				if (pRef2End + 1 < pNextRecord) {
					//const CTdbImgRef3 * const pRef3 = reinterpret_cast<const CTdbImgRef3 *> (pRef2End + 1);
				}

				break;
			}

			case 'T': { // ?? tail ??
				const CTdbTail * const pTail = static_cast<const CTdbTail *> (pHdr);
				// 41 78 74 03 46 43 2B 47 28 B8 F0 94 DA 2F 6F EA 75 59 0B F1
				break;
			}
		}

		pRecord = pNextRecord;

		SetProgressStatus (50.*(pRecord - _pData)/_cFileSize);
	} while (pRecord < pEnd);

	SetProgressStatus (50);

	_pMap->file_refs.swap (file_refs);

	// Suppress warning about empty Zoom etc.
	_pMap->bNoHeader = true;

	return lsOK;
}
